Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / Sideband.git / files / build_appimage.sh

Displaying Raw • Download


build_appimage.sh HEAD (8b8a5035) Text, 22.07 KB

T8b949e#!/bin/bash

T8b949e# Exit on error
Tffa657set -e

T8b949e# Configuration
Te6edf3SCRIPT_DIRTff7b72=Ta5d6ff"Tff7b72$(Tffa657cd Ta5d6ff"Tff7b72$(dirname Ta5d6ff"Tffd700${Te6edf3BASH_SOURCETb4b4b4[0]Tffd700}Ta5d6ff"Tff7b72)Ta5d6ff" Tff7b72&& Tffa657pwdTff7b72)Ta5d6ff"
Te6edf3BUILD_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3SCRIPT_DIRTffd700}Ta5d6ff/build/appimageTa5d6ff"
Te6edf3DIST_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3SCRIPT_DIRTffd700}Ta5d6ff/distTa5d6ff"
Te6edf3LXST_LIBS_DIRTff7b72=Ta5d6ff"Tffd700${Te6edf3SCRIPT_DIRTffd700}Ta5d6ff/../LXST/lib/staticTa5d6ff"
Te6edf3PYTHON_VERSIONTff7b72=Ta5d6ff"3.12"
Te6edf3PYTHON_TAGTff7b72=Ta5d6ff"cp312-cp312"
Te6edf3ARCHTff7b72=Ta5d6ff"x86_64"
Te6edf3LINUX_TAGTff7b72=Ta5d6ff"manylinux_2_28_x86_64"
Te6edf3MANYLINUX_YEARTff7b72=Ta5d6ff"2_28"
Te6edf3CLEANTff7b72=T79c0ff0
Te6edf3VERBOSETff7b72=T79c0ff0

T8b949e# Colors for output
Te6edf3REDTff7b72=Ta5d6ff'\033[0;31m'
Te6edf3GREENTff7b72=Ta5d6ff'\033[0;32m'
Te6edf3YELLOWTff7b72=Ta5d6ff'\033[1;33m'
Te6edf3BLUETff7b72=Ta5d6ff'\033[0;34m'
Te6edf3NCTff7b72=Ta5d6ff'\033[0m' T8b949e# No Color

T8b949e# Logging functions
log_infoTff7b72(Tff7b72) Tff7b72{
Tffa657echo -e Ta5d6ff"Tffd700${Te6edf3BLUETffd700}Ta5d6ff[INFO]Tffd700${Te6edf3NCTffd700}Ta5d6ff Te6edf3$1Ta5d6ff"
Tff7b72}

log_successTff7b72(Tff7b72) Tff7b72{
Tffa657echo -e Ta5d6ff"Tffd700${Te6edf3GREENTffd700}Ta5d6ff[SUCCESS]Tffd700${Te6edf3NCTffd700}Ta5d6ff Te6edf3$1Ta5d6ff"
Tff7b72}

log_warnTff7b72(Tff7b72) Tff7b72{
Tffa657echo -e Ta5d6ff"Tffd700${Te6edf3YELLOWTffd700}Ta5d6ff[WARN]Tffd700${Te6edf3NCTffd700}Ta5d6ff Te6edf3$1Ta5d6ff"
Tff7b72}

log_errorTff7b72(Tff7b72) Tff7b72{
Tffa657echo -e Ta5d6ff"Tffd700${Te6edf3REDTffd700}Ta5d6ff[ERROR]Tffd700${Te6edf3NCTffd700}Ta5d6ff Te6edf3$1Ta5d6ff"
Tff7b72}

T8b949e# Help message
show_helpTff7b72(Tff7b72) Tff7b72{
cat Ta5d6ff<< 'EOF'
Sideband AppImage Build Script
==============================
This script builds a self-contained AppImage distribution of Sideband
for Linux x86_64/aarch64 systems. It uses the python-appimage tool to
create a relocatable Python environment and bundles all dependencies.

Usage: ./build_appimage.sh [options]
-a, --arch ARCH Target architecture (x86_64 or aarch64), default: x86_64
-p, --python VERSION Python version to use, default: 3.12
-o, --output DIR Output directory for the AppImage, default: ./dist
-c, --clean Clean build directory before building
-v, --verbose Verbose output
-h, --help Show this help message

Examples:
./build_appimage.sh # Build for x86_64
./build_appimage.sh --arch aarch64 # Build for ARM64
./build_appimage.sh --clean # Clean and rebuild
make appimage # Build via Makefile
EOF
Tffa657exit T79c0ff0
Tff7b72}

parse_argsTff7b72(Tff7b72) Tff7b72{
Tff7b72while Tff7b72[Tff7b72[ Te6edf3$# -gt T79c0ff0 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72do
Tff7b72case Te6edf3$1 Tff7b72in
-aTb4b4b4|--archTff7b72)
Te6edf3ARCHTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
-pTb4b4b4|--pythonTff7b72)
Te6edf3PYTHON_VERSIONTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Te6edf3PYTHON_TAGTff7b72=Ta5d6ff"Ta5d6ffcpTffd700${Te6edf3PYTHON_VERSIONTb4b4b4//./Tffd700}Ta5d6ff-cpTffd700${Te6edf3PYTHON_VERSIONTb4b4b4//./Tffd700}Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
-oTb4b4b4|--outputTff7b72)
Te6edf3DIST_DIRTff7b72=Ta5d6ff"Te6edf3$2Ta5d6ff"
Tffa657shift T79c0ff2
Tb4b4b4;Tb4b4b4;
-cTb4b4b4|--cleanTff7b72)
Te6edf3CLEANTff7b72=T79c0ff1
Tffa657shift
Tb4b4b4;Tb4b4b4;
-vTb4b4b4|--verboseTff7b72)
Te6edf3VERBOSETff7b72=T79c0ff1
Tffa657shift
Tb4b4b4;Tb4b4b4;
-hTb4b4b4|--helpTff7b72)
show_help
Tb4b4b4;Tb4b4b4;
*Tff7b72)
log_error Ta5d6ff"Ta5d6ffUnknown option: Te6edf3$1Ta5d6ff"
show_help
Tffa657exit T79c0ff1
Tb4b4b4;Tb4b4b4;
Tff7b72esac
Tff7b72done

T8b949e# Set architecture-specific variables
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$ARCHTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"aarch64" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Te6edf3LINUX_TAGTff7b72=Ta5d6ff"manylinux_2_28_aarch64"
Te6edf3MANYLINUX_YEARTff7b72=Ta5d6ff"2_28"
Tff7b72elif Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$ARCHTa5d6ff" !Tff7b72= Ta5d6ff"x86_64" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_error Ta5d6ff"Ta5d6ffUnsupported architecture: Te6edf3$ARCHTa5d6ff"
log_error Ta5d6ff"Supported architectures: x86_64, aarch64"
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72}

check_depsTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Checking dependencies..."
Tff7b72if ! Tffa657command -v python3 Tb4b4b4&> /dev/nullTb4b4b4; Tff7b72then
log_error Ta5d6ff"python3 is required but not installed"
Tffa657exit T79c0ff1
Tff7b72fi
Tff7b72if ! python3 -m python_appimage --help Tb4b4b4&> /dev/nullTb4b4b4; Tff7b72then
log_error Ta5d6ff"python-appimage package is required"
log_error Ta5d6ff"Install with: pip3 install python-appimage"
Tffa657exit T79c0ff1
Tff7b72fi
T8b949e# Ensure appimagetool is available
Tff7b72if ! python3 -m python_appimage which appimagetool Tb4b4b4&> /dev/nullTb4b4b4; Tff7b72then
log_info Ta5d6ff"Installing appimagetool..."
python3 -m python_appimage install appimagetool
Tff7b72fi
log_success Ta5d6ff"Dependencies OK"
Tff7b72}

clean_buildTff7b72(Tff7b72) Tff7b72{
Tff7b72if Tff7b72[Tff7b72[ Te6edf3$CLEAN -eq T79c0ff1 Tff7b72]Tff7b72] Tff7b72|| Tff7b72[Tff7b72[ -d Ta5d6ff"Te6edf3$BUILD_DIRTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Cleaning build directory..."
rm -rf Ta5d6ff"Te6edf3$BUILD_DIRTa5d6ff"
log_success Ta5d6ff"Build directory cleaned"
Tff7b72fi
Tff7b72}

get_versionTff7b72(Tff7b72) Tff7b72{
Tffa657cd Ta5d6ff"Te6edf3$SCRIPT_DIRTa5d6ff"
python3 setup.py --getversion T79c0ff2>/dev/null Tff7b72|| Tffa657echo Ta5d6ff"unknown"
Tff7b72}

prepare_appdirTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Ta5d6ffPreparing AppDir with Python Te6edf3$PYTHON_VERSIONTa5d6ff (Te6edf3$LINUX_TAGTa5d6ff)...Ta5d6ff"
mkdir -p Ta5d6ff"Te6edf3$BUILD_DIRTa5d6ff"
Tffa657cd Ta5d6ff"Te6edf3$BUILD_DIRTa5d6ff"
T8b949e# Build the manylinux base if it doesn't exist
T8b949e# The directory name includes the full Python version (e.g., python3.12.13-cp312-cp312-...)
Tffa657local Te6edf3base_dir_patternTff7b72=Ta5d6ff"Ta5d6ffpythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff.*-Tffd700${Te6edf3PYTHON_TAGTffd700}Ta5d6ff-Tffd700${Te6edf3LINUX_TAGTffd700}Ta5d6ff"
Tffa657local Te6edf3base_dirTff7b72=Tff7b72$(find . -maxdepth T79c0ff1 -type d -name Ta5d6ff"Tffd700${Te6edf3base_dir_patternTffd700}Ta5d6ff" Tb4b4b4| head -1 Tb4b4b4| sed Ta5d6ff's|^\./||'Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$base_dirTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Tff7b72[Tff7b72[ ! -d Ta5d6ff"Te6edf3$base_dirTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Building Python base image (this may take a while)..."
python3 -m python_appimage build manylinux Ta5d6ff"Tffd700${Te6edf3MANYLINUX_YEARTffd700}Ta5d6ff_Tffd700${Te6edf3ARCHTffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3PYTHON_TAGTffd700}Ta5d6ff" -n
T8b949e# Find the newly created directory
Te6edf3base_dirTff7b72=Tff7b72$(find . -maxdepth T79c0ff1 -type d -name Ta5d6ff"Tffd700${Te6edf3base_dir_patternTffd700}Ta5d6ff" Tb4b4b4| head -1 Tb4b4b4| sed Ta5d6ff's|^\./||'Tff7b72)
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$base_dirTa5d6ff" Tff7b72]Tff7b72] Tff7b72|| Tff7b72[Tff7b72[ ! -d Ta5d6ff"Te6edf3$base_dirTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_error Ta5d6ff"Could not find or create Python base directory"
Tffa657exit T79c0ff1
Tff7b72fi
log_info Ta5d6ff"Ta5d6ffUsing base directory: Te6edf3$base_dirTa5d6ff"
T8b949e# Copy to AppDir
Tff7b72if Tff7b72[Tff7b72[ -d Ta5d6ff"AppDir" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
rm -rf AppDir
Tff7b72fi
cp -r Ta5d6ff"Te6edf3$base_dirTa5d6ff" AppDir
log_success Ta5d6ff"AppDir prepared"
Tff7b72}

install_pip_depsTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Installing Python dependencies..."
Tffa657local Te6edf3apprunTff7b72=Ta5d6ff"./AppDir/AppRun"
Tffa657local Te6edf3site_packagesTff7b72=Ta5d6ff"Ta5d6ff./AppDir/opt/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/lib/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/site-packagesTa5d6ff"
T8b949e# Core dependencies
T8b949e# pysdl2-dll provides manylinux2014-compatible SDL2 libraries
Tffa657local Te6edf3edepsTff7b72=Tff7b72(
Ta5d6ff"kivy>=2.3.0"
Ta5d6ff"numpy>=2.3.4"
Ta5d6ff"pillow>=10.2.0"
Ta5d6ff"mistune>=3.0.2"
Ta5d6ff"qrcode"
Ta5d6ff"materialyoucolor>=2.0.7"
Ta5d6ff"beautifulsoup4"
Ta5d6ff"pycodec2>=4.1.0"
Ta5d6ff"cffi>=2.0.0"
Ta5d6ff"cryptography>=3.4.7"
Ta5d6ff"pyserial>=3.5"
Ta5d6ff"pysdl2-dll>=2.32.0"
Ta5d6ff"dbus-python"
Tff7b72)

Tffa657local Te6edf3ldepsTff7b72=Tff7b72(
Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/../../lib/whl/rns-*-py3-none-any.whlTa5d6ff"
Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/../../lib/whl/lxmf-*-py3-none-any.whlTa5d6ff"
Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/../../lib/whl/lxst-*-py3-none-any.whlTa5d6ff"
Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/../../dist/sbapp-*-py3-none-any.whlTa5d6ff"
Tff7b72)
Tff7b72for dep Tff7b72in Ta5d6ff"Tffd700${Te6edf3edepsTb4b4b4[@]Tffd700}Ta5d6ff"Tb4b4b4; Tff7b72do
log_info Ta5d6ff"Ta5d6ffInstalling Te6edf3$depTa5d6ff...Ta5d6ff"
Te6edf3$apprun -m pip install --no-warn-script-location Ta5d6ff"Te6edf3$depTa5d6ff"
Tff7b72done
Tff7b72for dep Tff7b72in Ta5d6ff"Tffd700${Te6edf3ldepsTb4b4b4[@]Tffd700}Ta5d6ff"Tb4b4b4; Tff7b72do
log_info Ta5d6ff"Ta5d6ffInstalling Te6edf3$depTa5d6ff...Ta5d6ff"
Te6edf3$apprun -m pip install --no-warn-script-location Te6edf3$dep
Tff7b72done
log_success Ta5d6ff"Python dependencies installed"
Tff7b72}

copy_local_packagesTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Copying local packages..."
T8b949e# local site_packages="${BUILD_DIR}/AppDir/opt/python${PYTHON_VERSION}/lib/python${PYTHON_VERSION}/site-packages"

T8b949e# log_info "Copying sbapp..."
T8b949e# mkdir -p "$site_packages/sbapp"
T8b949e# tar -C "${SCRIPT_DIR}/sbapp" -cf - . 2>/dev/null | tar -C "$site_packages/sbapp" -xf -

T8b949e# for module in mapview kivymd md plyer pmqtt; do
T8b949e# if [[ -d "${SCRIPT_DIR}/sbapp/$module" ]]; then
T8b949e# log_info "Copying $module..."
T8b949e# rm -rf "$site_packages/$module"
T8b949e# cp -r "${SCRIPT_DIR}/sbapp/$module" "$site_packages/"
T8b949e# fi
T8b949e# done

T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-311-aarch64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-311-x86_64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-312-aarch64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-312-x86_64-linux-gnu.so" "$site_packages/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-313-aarch64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-313-x86_64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-314-aarch64-linux-gnu.so" "$site_packages/sbapp/LXST"
T8b949e# cp "$LXST_LIBS_DIR/filterlib.cpython-314-x86_64-linux-gnu.so" "$site_packages/sbapp/LXST"

log_success Ta5d6ff"Local packages copied"
Tff7b72}

clean_sbappTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Cleaning sbapp package..."
Tffa657local Te6edf3sbapp_dirTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/AppDir/opt/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/lib/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/site-packages/sbappTa5d6ff"

rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/.buildozerTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/.gradleTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/binTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/buildTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/distTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/patchesTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/servicesTa5d6ff"
rm -rf Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/__pycache__Ta5d6ff"
rm -f Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/buildozer.specTa5d6ff"
rm -f Ta5d6ff"Te6edf3$sbapp_dirTa5d6ff/MakefileTa5d6ff"
log_success Ta5d6ff"sbapp cleaned"
Tff7b72}

bundle_native_libsTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Bundling native libraries..."
Tffa657local Te6edf3lib_dirTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/AppDir/usr/libTa5d6ff"
mkdir -p Ta5d6ff"Te6edf3$lib_dirTa5d6ff"
T8b949e# Extract pysdl2-dll libraries (manylinux2014 compatible = glibc 2.17)
Tffa657local Te6edf3temp_dirTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/temp_pysdl2Ta5d6ff"
mkdir -p Ta5d6ff"Te6edf3$temp_dirTa5d6ff"
log_info Ta5d6ff"Downloading pysdl2-dll for compatible SDL2 libraries..."
T8b949e# Download pysdl2-dll wheel (manylinux2014 compatible)
python3 -m pip download pysdl2-dll --platform manylinux2014_x86_64 --only-binaryTff7b72=:all: -d Ta5d6ff"Te6edf3$temp_dirTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffea00\
python3 -m pip download pysdl2-dll -d Ta5d6ff"Te6edf3$temp_dirTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
T8b949e# Find the downloaded wheel
Tffa657local Te6edf3wheel_fileTff7b72=Tff7b72$(find Ta5d6ff"Te6edf3$temp_dirTa5d6ff" -name Ta5d6ff"pysdl2_dll*.whl" Tb4b4b4| head -1Tff7b72)
Tffa657local Te6edf3sdl2_sourceTff7b72=Ta5d6ff""
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$wheel_fileTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Extracting SDL2 libraries from pysdl2-dll..."
unzip -q Ta5d6ff"Te6edf3$wheel_fileTa5d6ff" -d Ta5d6ff"Te6edf3$temp_dirTa5d6ff/extractedTa5d6ff"
Te6edf3sdl2_sourceTff7b72=Ta5d6ff"Te6edf3$temp_dirTa5d6ff/extracted/sdl2dll/dllTa5d6ff"
Tff7b72fi
Tff7b72if Tff7b72[Tff7b72[ -d Ta5d6ff"Te6edf3$sdl2_sourceTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Copying SDL2 libraries from pysdl2-dll..."
T8b949e# Core SDL2 libraries
Tffa657local Te6edf3sdl2_libsTff7b72=Tff7b72(
Ta5d6ff"libSDL2-2.0.so"
Ta5d6ff"libSDL2-2.0.so.0"
Ta5d6ff"libSDL2_gfx-1.0.so"
Ta5d6ff"libSDL2_image-2.0.so"
Ta5d6ff"libSDL2_mixer-2.0.so"
Ta5d6ff"libSDL2_ttf-2.0.so"
Tff7b72)
T8b949e# Audio codec libraries
Tffa657local Te6edf3audio_libsTff7b72=Tff7b72(
Ta5d6ff"libopus.so.0"
Ta5d6ff"libopusfile.so.0"
Ta5d6ff"libogg.so.0"
Tff7b72)
T8b949e# Image format libraries
Tffa657local Te6edf3image_libsTff7b72=Tff7b72(
Ta5d6ff"libwebp.so.7"
Ta5d6ff"libtiff.so.5"
Tff7b72)
T8b949e# Copy all available libraries
Tff7b72for lib Tff7b72in Ta5d6ff"Tffd700${Te6edf3sdl2_libsTb4b4b4[@]Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3audio_libsTb4b4b4[@]Tffd700}Ta5d6ff" Ta5d6ff"Tffd700${Te6edf3image_libsTb4b4b4[@]Tffd700}Ta5d6ff"Tb4b4b4; Tff7b72do
Tff7b72if Tff7b72[Tff7b72[ -f Ta5d6ff"Te6edf3$sdl2_sourceTa5d6ff/Te6edf3$libTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
cp -L Ta5d6ff"Te6edf3$sdl2_sourceTa5d6ff/Te6edf3$libTa5d6ff" Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Ta5d6ff" T79c0ff2>/dev/null
log_info Ta5d6ff"Ta5d6ff Copied Te6edf3$libTa5d6ff"
Tff7b72fi
Tff7b72done
T8b949e# Copy any additional .so files that might be needed
Tff7b72for so_file Tff7b72in Ta5d6ff"Te6edf3$sdl2_sourceTa5d6ff"/*.so*Tb4b4b4; Tff7b72do
Tff7b72if Tff7b72[Tff7b72[ -f Ta5d6ff"Te6edf3$so_fileTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657local Te6edf3basenameTff7b72=Tff7b72$(basename Ta5d6ff"Te6edf3$so_fileTa5d6ff"Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ ! -f Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Te6edf3$basenameTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
cp -L Ta5d6ff"Te6edf3$so_fileTa5d6ff" Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Ta5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
Tff7b72fi
Tff7b72fi
Tff7b72done
log_success Ta5d6ff"SDL2 libraries copied from pysdl2-dll"
Tff7b72else
log_warn Ta5d6ff"Could not find pysdl2-dll libraries, falling back to system libraries"
T8b949e# Fallback: copy SDL2 from system
Tffa657local Te6edf3sdl2_fallback_libsTff7b72=Tff7b72(
Ta5d6ff"libSDL2-2.0.so.0"
Ta5d6ff"libSDL2_image-2.0.so.0"
Ta5d6ff"libSDL2_ttf-2.0.so.0"
Ta5d6ff"libSDL2_mixer-2.0.so.0"
Tff7b72)
Tff7b72for lib Tff7b72in Ta5d6ff"Tffd700${Te6edf3sdl2_fallback_libsTb4b4b4[@]Tffd700}Ta5d6ff"Tb4b4b4; Tff7b72do
Tffa657local Te6edf3lib_pathTff7b72=Tff7b72$(ldconfig -p Tb4b4b4| grep Ta5d6ff"Te6edf3$libTa5d6ff" Tb4b4b4| head -1 Tb4b4b4| awk Ta5d6ff'{print $NF}'Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$lib_pathTa5d6ff" Tff7b72]Tff7b72] Tff7b72&& Tff7b72[Tff7b72[ -f Ta5d6ff"Te6edf3$lib_pathTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Ta5d6ff Copying system Te6edf3$libTa5d6ff"
cp -L Ta5d6ff"Te6edf3$lib_pathTa5d6ff" Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Ta5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
Tff7b72fi
Tff7b72done
Tff7b72fi
T8b949e# Clean up temp directory
rm -rf Ta5d6ff"Te6edf3$temp_dirTa5d6ff"
T8b949e# Handle codec2 separately - copy from system with compatibility warning
log_info Ta5d6ff"Checking for codec2 library..."
Tffa657local Te6edf3codec2_pathTff7b72=Tff7b72$(ldconfig -p Tb4b4b4| grep Ta5d6ff"libcodec2.so.1" Tb4b4b4| head -1 Tb4b4b4| awk Ta5d6ff'{print $NF}'Tff7b72)
Tff7b72if Tff7b72[Tff7b72[ -n Ta5d6ff"Te6edf3$codec2_pathTa5d6ff" Tff7b72]Tff7b72] Tff7b72&& Tff7b72[Tff7b72[ -f Ta5d6ff"Te6edf3$codec2_pathTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_info Ta5d6ff"Copying codec2 from system"
cp -L Ta5d6ff"Te6edf3$codec2_pathTa5d6ff" Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Ta5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
T8b949e# Also copy versioned files
Tff7b72for linked_lib Tff7b72in Tff7b72$(find Tff7b72$(dirname Ta5d6ff"Te6edf3$codec2_pathTa5d6ff"Tff7b72) -name Ta5d6ff"libcodec2.so*" -type f -o -type l T79c0ff2>/dev/null Tb4b4b4| head -3Tff7b72)Tb4b4b4; Tff7b72do
cp -L Ta5d6ff"Te6edf3$linked_libTa5d6ff" Ta5d6ff"Te6edf3$lib_dirTa5d6ff/Ta5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
Tff7b72done
Tff7b72else
log_warn Ta5d6ff"codec2 library not found - voice features will be unavailable"
Tff7b72fi
log_success Ta5d6ff"Native libraries bundled"
Tff7b72}

configure_apprunTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Configuring AppRun..."
Tffa657local Te6edf3apprunTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/AppDir/AppRunTa5d6ff"
cat > Ta5d6ff"Te6edf3$apprunTa5d6ff" Ta5d6ff<< 'EOF'

if [ -z "${APPIMAGE}" ]; then
export ARGV0="$0"
self=$(readlink -f -- "$0")
here="${self%/*}"
tmp="${here%/*}"
export APPDIR="${tmp%/*}"
fi

export APPIMAGE_COMMAND=$(command -v -- "$ARGV0")

export TCL_LIBRARY="${APPDIR}/usr/share/tcltk/tcl8.6"
export TK_LIBRARY="${APPDIR}/usr/share/tcltk/tk8.6"
export TKPATH="${TK_LIBRARY}"

export SSL_CERT_FILE="${APPDIR}/opt/_internal/certs.pem"

export KIVY_SDL2_PATH="${APPDIR}/usr/lib"

export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${LD_LIBRARY_PATH}"

export PYTHONPATH="${APPDIR}/opt/python3.12/lib/python3.12/site-packages:${PYTHONPATH}"

"${APPDIR}/opt/python3.12/bin/python3.12" -m sbapp.main "$@"
EOF
chmod +x Ta5d6ff"Te6edf3$apprunTa5d6ff"
log_success Ta5d6ff"AppRun configured"
Tff7b72}

configure_desktopTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Configuring desktop integration..."
Tffa657local Te6edf3appdirTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/AppDirTa5d6ff"
T8b949e# Remove old Python desktop files and AppStream metadata
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/pythonTa5d6ff"*.desktop
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applications/pythonTa5d6ff"*.desktop
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/metainfo/pythonTa5d6ff"*.appdata.xml
T8b949e# Create desktop file
cat > Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.desktopTa5d6ff" Ta5d6ff<< EOF
[Desktop Entry]
Name=Sideband
Comment=Messaging, telemetry and remote control over LXMF
Exec=AppRun
Icon=sideband
Categories=Utility;Network;
Terminal=false
Type=Application
Version=1.0
X-AppImage-Name=Sideband
X-AppImage-Version=${VERSION}
EOF
T8b949e# Copy icon
cp Ta5d6ff"Tffd700${Te6edf3SCRIPT_DIRTffd700}Ta5d6ff/sbapp/assets/icon.pngTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.pngTa5d6ff"
cp Ta5d6ff"Tffd700${Te6edf3SCRIPT_DIRTffd700}Ta5d6ff/sbapp/assets/icon_256.pngTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband_256.pngTa5d6ff"
T8b949e# Set up symlinks for AppImage integration
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/.DirIconTa5d6ff"
ln -sf sideband.png Ta5d6ff"Te6edf3$appdirTa5d6ff/.DirIconTa5d6ff"
T8b949e# Install to usr/share directories
mkdir -p Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applicationsTa5d6ff"
mkdir -p Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/icons/hicolor/256x256/appsTa5d6ff"
mkdir -p Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/icons/hicolor/512x512/appsTa5d6ff"
cp Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.desktopTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applications/Ta5d6ff"
find Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/iconsTa5d6ff" -name Ta5d6ff"python.png" -delete T79c0ff2>/dev/null Tff7b72|| Tffa657true
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/python.pngTa5d6ff"
cp Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband_256.pngTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/icons/hicolor/256x256/apps/Ta5d6ff"
cp Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.pngTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/icons/hicolor/512x512/apps/Ta5d6ff"
T8b949e# Create minimal AppStream metadata to satisfy appimagetool
mkdir -p Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/metainfoTa5d6ff"
cat > Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/metainfo/io.unsigned.sideband.metainfo.xmlTa5d6ff" Ta5d6ff<< EOF
?xml version="1.0" encoding="UTF-8"?>
component type="desktop-application">
<id>io.unsigned.sideband</id>
<metadata_license>MIT</metadata_license>
<name>Sideband</name>
<summary>LXMF client for Reticulum networks</summary>
<description>
<p>Sideband is an LXMF client for Android, Linux and macOS allowing you to communicate with people or LXMF-compatible systems over Reticulum networks using LoRa, Packet Radio, WiFi, I2P, or anything else Reticulum supports.</p>
</description>
<url type="homepage">https://unsigned.io/sideband</url>
<launchable type="desktop-id">io.unsigned.sideband.desktop</launchable>
<provides>
<binary>sideband</binary>
</provides>
/component>
EOF
T8b949e# Rename desktop file to match AppStream ID
mv Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applications/sideband.desktopTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applications/io.unsigned.sideband.desktopTa5d6ff"
cp Ta5d6ff"Te6edf3$appdirTa5d6ff/usr/share/applications/io.unsigned.sideband.desktopTa5d6ff" Ta5d6ff"Te6edf3$appdirTa5d6ff/io.unsigned.sideband.desktopTa5d6ff"
rm -f Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.desktopTa5d6ff"
ln -sf io.unsigned.sideband.desktop Ta5d6ff"Te6edf3$appdirTa5d6ff/sideband.desktopTa5d6ff" T79c0ff2>/dev/null Tff7b72|| Tffa657true
log_success Ta5d6ff"Desktop integration configured"
Tff7b72}

patch_sitecustomizeTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Patching sitecustomize.py..."
Tffa657local Te6edf3sitecustomizeTff7b72=Ta5d6ff"Tffd700${Te6edf3BUILD_DIRTffd700}Ta5d6ff/AppDir/opt/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/lib/pythonTffd700${Te6edf3PYTHON_VERSIONTffd700}Ta5d6ff/site-packages/sitecustomize.pyTa5d6ff"
T8b949e# Add our path hook at the beginning
cat > Ta5d6ff"Te6edf3$sitecustomizeTa5d6ff" Ta5d6ff<< 'EOF'
'''Python AppImage hooks for Sideband
'''
import atexit
import os
import sys

_appdir = os.getenv('APPDIR')
if _appdir:
_site_packages = os.path.join(_appdir, 'opt/python3.12/lib/python3.12/site-packages')
if _site_packages not in sys.path:
sys.path.insert(0, _site_packages)

_bin_at_start = os.listdir(sys.prefix + '/bin') if os.path.isdir(sys.prefix + '/bin') else []

def patch_pip_install():
'''Change absolute shebangs to relative ones following a `pip` install
'''
if not 'pip' in sys.modules:
return

appdir = os.getenv('APPDIR')
if not appdir:
return
python_x_y = 'python{:}.{:}'.format(*sys.version_info[:2])
if sys.prefix != '{:}/opt/{:}'.format(appdir, python_x_y):
return

args = sys.argv[1:]
if 'install' in args:
for exe in os.listdir(sys.prefix + '/bin'):
path = os.path.join(sys.prefix, 'bin', exe)

if (not os.path.isfile(path)) or (not os.access(path, os.X_OK)) or \
exe.startswith('python') or os.path.islink(path) or \
exe.endswith('.pyc') or exe.endswith('.pyo'):
continue

try:
with open(path, 'r') as f:
header = f.read(2)
if header != '#!':
continue
content = f.read()
except:
continue

shebang, body = content.split(os.linesep, 1)
shebang = shebang.strip().split()
executable = shebang.pop(0)
if executable != sys.executable:
head, altbody = body.split(os.linesep, 1)
if head.startswith("'''exec' /"): # Patch for alt shebang
body = altbody.split(os.linesep, 1)[1]
executable = head.split()[1]
if executable != sys.executable:
continue
else:
continue

relpath = os.path.relpath(
sys.prefix + '/../../usr/bin/' + python_x_y,
sys.prefix + '/bin')
shebang.append('"$@"')
cmd = (
'"exec"',
'"$(dirname $(readlink -f ${0}))/' + relpath + '"',
'"$0"',
' '.join(shebang)
)

try:
with open(path, 'w') as f:
f.write('#! /bin/sh\n')
f.write(' '.join(cmd) + '\n')
f.write(body)
except IOError:
continue

if exe in _bin_at_start:
continue

usr_dir = os.path.join(sys.prefix, '../../usr/bin')
usr_exe = os.path.join(usr_dir, exe)
if not os.path.exists(usr_exe):
relpath = os.path.relpath(path, usr_dir)
os.symlink(relpath, usr_exe)

elif 'uninstall' in args:
usr_dir = os.path.join(sys.prefix, '../../usr/bin')
if os.path.isdir(usr_dir):
for exe in os.listdir(usr_dir):
path = os.path.join(usr_dir, exe)
if (not os.path.islink(path)) or \
os.path.exists(os.path.realpath(path)):
continue
os.remove(path)


if os.getenv('VIRTUAL_ENV') is None:
atexit.register(patch_pip_install)
else:
del _bin_at_start
del patch_pip_install
EOF
log_success Ta5d6ff"sitecustomize.py patched"
Tff7b72}

build_appimageTff7b72(Tff7b72) Tff7b72{
log_info Ta5d6ff"Building AppImage..."
mkdir -p Ta5d6ff"Te6edf3$DIST_DIRTa5d6ff"
Tffa657local Te6edf3versionTff7b72=Tff7b72$(get_versionTff7b72)
Tffa657local Te6edf3output_nameTff7b72=Ta5d6ff"Ta5d6ffSideband_Tffd700${Te6edf3versionTffd700}Ta5d6ff_Tffd700${Te6edf3ARCHTffd700}Ta5d6ff.appimageTa5d6ff"
Tffa657cd Ta5d6ff"Te6edf3$BUILD_DIRTa5d6ff"
T8b949e# Get appimagetool path
Tffa657local appimagetool
Te6edf3appimagetoolTff7b72=Tff7b72$(python3 -m python_appimage which appimagetoolTff7b72)
Tff7b72if Tff7b72[Tff7b72[ -z Ta5d6ff"Te6edf3$appimagetoolTa5d6ff" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
log_error Ta5d6ff"appimagetool not found"
Tffa657exit T79c0ff1
Tff7b72fi
T8b949e# Build the AppImage
Te6edf3ARCHTff7b72=Ta5d6ff"Te6edf3$ARCHTa5d6ff" Ta5d6ff"Te6edf3$appimagetoolTa5d6ff" AppDir Ta5d6ff"Te6edf3$output_nameTa5d6ff" T79c0ff2>Tb4b4b4&T79c0ff1 Tb4b4b4| Tff7b72while Tffa657read lineTb4b4b4; Tff7b72do
Tff7b72if Tff7b72[Tff7b72[ Te6edf3$VERBOSE -eq T79c0ff1 Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
Tffa657echo Ta5d6ff"Te6edf3$lineTa5d6ff"
Tff7b72fi
Tff7b72done
T8b949e# Move to dist directory
mv Ta5d6ff"Te6edf3$output_nameTa5d6ff" Ta5d6ff"Te6edf3$DIST_DIRTa5d6ff/Ta5d6ff"
log_success Ta5d6ff"Ta5d6ffAppImage built: Tffd700${Te6edf3DIST_DIRTffd700}Ta5d6ff/Tffd700${Te6edf3output_nameTffd700}Ta5d6ff"
T8b949e# Show file info
ls -lh Ta5d6ff"Tffd700${Te6edf3DIST_DIRTffd700}Ta5d6ff/Tffd700${Te6edf3output_nameTffd700}Ta5d6ff"
Tff7b72}

T8b949e# Main build process
mainTff7b72(Tff7b72) Tff7b72{
T8b949e# Handle help before anything else
Tff7b72for arg Tff7b72in Ta5d6ff"Te6edf3$@Ta5d6ff"Tb4b4b4; Tff7b72do
Tff7b72if Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$argTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"-h" Tff7b72]Tff7b72] Tff7b72|| Tff7b72[Tff7b72[ Ta5d6ff"Te6edf3$argTa5d6ff" Tff7b72=Tff7b72= Ta5d6ff"--help" Tff7b72]Tff7b72]Tb4b4b4; Tff7b72then
show_help
Tff7b72fi
Tff7b72done
log_info Ta5d6ff"Sideband AppImage Builder"
log_info Ta5d6ff"========================="
parse_args Ta5d6ff"Te6edf3$@Ta5d6ff"
log_info Ta5d6ff"Configuration:"
log_info Ta5d6ff"Ta5d6ff Architecture: Te6edf3$ARCHTa5d6ff"
log_info Ta5d6ff"Ta5d6ff Python Version: Te6edf3$PYTHON_VERSIONTa5d6ff"
log_info Ta5d6ff"Ta5d6ff Build Directory: Te6edf3$BUILD_DIRTa5d6ff"
log_info Ta5d6ff"Ta5d6ff Output Directory: Te6edf3$DIST_DIRTa5d6ff"
Te6edf3VERSIONTff7b72=Tff7b72$(get_versionTff7b72)
log_info Ta5d6ff"Ta5d6ff Sideband Version: Te6edf3$VERSIONTa5d6ff"
check_deps
clean_build
prepare_appdir
install_pip_deps
copy_local_packages
clean_sbapp
bundle_native_libs
configure_apprun
configure_desktop
patch_sitecustomize
build_appimage
log_success Ta5d6ff"Build complete!"
log_info Ta5d6ff"Ta5d6ffOutput: Tffd700${Te6edf3DIST_DIRTffd700}Ta5d6ff/Sideband-Tffd700${Te6edf3VERSIONTffd700}Ta5d6ff-Tffd700${Te6edf3ARCHTffd700}Ta5d6ff.AppImageTa5d6ff"
Tff7b72}

main Ta5d6ff"Te6edf3$@Ta5d6ff"


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────